#include <Bust/Threads/Condition.hpp>
#include "PrivateData.hpp"

#include <sys/time.h>
#include <cassert>

namespace bust {

void condition_cleanup_handler( void *arg ) {
	pthread_mutex_t *mutex = static_cast< pthread_mutex_t * > ( arg );
	pthread_mutex_unlock( mutex );
}

Condition::Condition() {
	ConditionPrivateData *pd = new ConditionPrivateData();
	int status = pthread_cond_init( &pd->condition, NULL );
	assert(status == 0);
	_data = static_cast< void * > ( pd );
}

Condition::~Condition() {
	ConditionPrivateData *pd = static_cast< ConditionPrivateData * > ( _data );
	int status = pthread_cond_destroy( &pd->condition );
	assert(status == 0);
	delete pd;
}

int Condition::wait( Mutex &mutex ) {
	ConditionPrivateData *pd = static_cast< ConditionPrivateData * > ( _data );
	MutexPrivateData *mpd = static_cast< MutexPrivateData * > ( mutex._data );
	int status;
	pthread_cleanup_push(condition_cleanup_handler, &mpd->mutex);
			status = pthread_cond_wait( &pd->condition, &mpd->mutex );
			pthread_cleanup_pop(0);
	return status;
}

int Condition::wait( Mutex &mutex, unsigned long int ms ) {
	ConditionPrivateData *pd = static_cast< ConditionPrivateData * > ( _data );
	MutexPrivateData *mpd = static_cast< MutexPrivateData * > ( mutex._data );
	struct timeval now;
	gettimeofday( &now, 0 );
	unsigned int sec = ms / 1000;
	unsigned int nsec = ( ms % 1000 ) * 1000;
	struct timespec abstime;
	abstime.tv_sec = now.tv_sec + sec;
	abstime.tv_nsec = now.tv_usec * 1000 + nsec;
	int status;
	pthread_cleanup_push(condition_cleanup_handler, &mpd->mutex);
			status = pthread_cond_timedwait( &pd->condition, &mpd->mutex,
					&abstime );
			pthread_cleanup_pop(0);
	return status;

}

int Condition::signal() {

	ConditionPrivateData *pd = static_cast< ConditionPrivateData * > ( _data );

	return pthread_cond_signal( &pd->condition );
}

int Condition::broadcast() {

	ConditionPrivateData *pd = static_cast< ConditionPrivateData * > ( _data );

	return pthread_cond_broadcast( &pd->condition );
}

}
